package com.zkadmin.util;

import java.util.Comparator;
import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SortEvent;
import org.zkoss.zul.Column;
import org.zkoss.zul.Columns;
import org.zkoss.zul.Grid;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Paging;
import org.zkoss.zul.event.PagingEvent;
import org.zkoss.zul.event.ZulEvents;
import org.zkoss.zul.impl.MeshElement;

import com.mysema.query.types.Predicate;
import com.zkadmin.service.SysBaseService;

public class PagingWrap<T> {
	private SysBaseService<T> service;
	private final MeshElement mesh;
	private final Paging paging;
	private Predicate predicate;
	private Sort sort;

	public PagingWrap(final SysBaseService<T> service, final MeshElement mesh, final Paging paging) {
		this(service, mesh, paging, null, null);
	}

	public PagingWrap(final SysBaseService<T> service, final MeshElement mesh, final Paging paging, final Predicate predicate) {
		this(service, mesh, paging, predicate, null);
	}

	public PagingWrap(final SysBaseService<T> service, final MeshElement mesh, final Paging paging, final Predicate predicate, final Sort sort) {
		this.service = service;
		this.mesh = mesh;
		this.paging = paging;
		this.predicate = predicate;
		this.sort = sort;
	}

	public void paging() {
		paging.setDetailed(true);
		if (paging.getPageSize() <= 0) {
			paging.setPageSize(AdminConstants.PAGE_SIZE);
		}
		paging.addEventListener(ZulEvents.ON_PAGING, new EventListener<PagingEvent>() {
			@Override
			public void onEvent(PagingEvent event) throws Exception {
				paging.setActivePage(event.getActivePage());
				refreshData();
			}
		});

		if (mesh instanceof Listbox) {
			Listhead listhead = ((Listbox) mesh).getListhead();
			List<Listheader> listheaders = listhead.getChildren();
			for (Listheader listheader : listheaders) {
				if (listheader.getSortAscending() != null && listheader.getSortDescending() != null) {
					listheader.addEventListener(Events.ON_SORT, new SortEventListener());
				}
			}
		} else if (mesh instanceof Grid) {
			Columns columns = ((Grid) mesh).getColumns();
			List<Column> columnList = columns.getChildren();
			for (Column column : columnList) {
				if (column.getSortAscending() != null && column.getSortDescending() != null) {
					column.addEventListener(Events.ON_SORT, new SortEventListener());
				}
			}
		}
		refreshData();
	}

	private void refreshData() {
		Page<T> page = service.paging(predicate, new PageRequest(paging.getActivePage(), paging.getPageSize(), sort));
		paging.setTotalSize((int) page.getTotalElements());
		if (mesh instanceof Grid) {
			Grid grid = (Grid) mesh;
			grid.setEmptyMessage(AdminConstants.getEmptyMessage());
			grid.setModel(new ListModelList<T>(page.getContent()));
		} else if (mesh instanceof Listbox) {
			Listbox listbox = (Listbox) mesh;
			listbox.setEmptyMessage(AdminConstants.getEmptyMessage());
			ListModelList<T> modelList = new ListModelList<T>(page.getContent());
			modelList.setMultiple(listbox.isMultiple());
			listbox.setModel(modelList);
		}
	}

	class SortEventListener implements EventListener<SortEvent> {
		@Override
		public void onEvent(SortEvent event) throws Exception {
			Listheader listheader = (Listheader) event.getTarget();
			final String sortDirection = listheader.getSortDirection();
			if ("ascending".equals(sortDirection)) {
				final Comparator<?> cmpr = listheader.getSortDescending();
				if (cmpr instanceof OrderByComparator) {
					sort = new Sort(new Order(Direction.ASC, ((OrderByComparator) cmpr).getColumnName()));
				}
			} else if ("descending".equals(sortDirection) || "natural".equals(sortDirection)) {
				final Comparator<?> cmpr = listheader.getSortAscending();
				if (cmpr instanceof OrderByComparator) {
					sort = new Sort(new Order(Direction.DESC, ((OrderByComparator) cmpr).getColumnName()));
				}
			}
			refreshData();
		}
	}
}
